

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include "hal_sd.h"

#define SPI_DMA_CHAN    2
#define MOUNT_POINT "/sdcard"
static const char *TAG = "SD";

static uint8_t sdMountStatus = 0;
static sdmmc_card_t *sdcard;
static sdmmc_host_t sdhost = SDSPI_HOST_DEFAULT();


void hal_sd_spi_init()
{
    esp_err_t ret;

    spi_bus_config_t bus_cfg = {
        .mosi_io_num = SD_PIN_NUM_MOSI,
        .miso_io_num = SD_PIN_NUM_MISO,
        .sclk_io_num = SD_PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 4000,
    };
    ret = spi_bus_initialize(sdhost.slot, &bus_cfg, SPI_DMA_CHAN);

    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to initialize bus.");
        return;
    }

    gpio_config_t io_conf = {};
    io_conf.intr_type = GPIO_INTR_DISABLE;
    //set as output mode
    io_conf.mode = GPIO_MODE_DEF_INPUT;
    //bit mask of the pins that you want to set,e.g.GPIO18/19
    io_conf.pin_bit_mask = (1ULL<<SD_PIN_NUM_CHECK);
    //disable pull-down mode
    io_conf.pull_down_en = 0;
    //disable pull-up mode
    io_conf.pull_up_en = 0;
    //configure GPIO with the given settings
    gpio_config(&io_conf);

    #ifdef PRJ_UNUSED
    sd_mount();
    #endif
}

void sd_mount()
{
    esp_err_t ret;
    //const char mount_point[] = MOUNT_POINT;
    
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = true,
        .max_files = 5,
        .allocation_unit_size = 16 * 1024
    };

    ESP_LOGI(TAG, "Initializing SD card");

    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = SD_PIN_NUM_CS;
    slot_config.host_id = sdhost.slot;

    ESP_LOGI(TAG, "Mounting filesystem");
    ret = esp_vfs_fat_sdspi_mount(MOUNT_POINT, &sdhost, &slot_config, &mount_config, &sdcard);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount filesystem. "
                     "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
        } else {
            ESP_LOGE(TAG, "Failed to initialize the card (%s). "
                     "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
        }
        return;
    }
    ESP_LOGI(TAG, "Filesystem mounted");
    sdMountStatus = 1;
    // Card has been initialized, print its properties
    sdmmc_card_print_info(stdout, sdcard);
}

void sd_unmont()
{
    if(sdMountStatus)
    {
        // All done, unmount partition and disable SPI peripheral
        esp_vfs_fat_sdcard_unmount(MOUNT_POINT, sdcard);
        ESP_LOGI(TAG, "Card unmounted");

        //deinitialize the bus after all devices are removed
        spi_bus_free(sdhost.slot);
        sdMountStatus = 0;
    }
}


void sd_check()
{
    static uint8_t sd_status = 0;
    #ifndef PRJ_UNUSED
    if(gpio_get_level(SD_PIN_NUM_CHECK) != sd_status)
    {
        if(!gpio_get_level(SD_PIN_NUM_CHECK))
        {
            if(sdMountStatus)
            {
                sd_unmont();
            } 
        }
        else
        {
            if(!sdMountStatus)
            {
                sd_mount();
            }
        }
        sd_status = gpio_get_level(SD_PIN_NUM_CHECK);
    }
    #endif
}

uint8_t sdgetStatus()
{
    return sdMountStatus;
}

  